home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / wvnsrc75.zip / WVGROUP.C < prev    next >
C/C++ Source or Header  |  1993-02-03  |  32KB  |  1,166 lines

  1. /*---  wvgroup.c ------------------------------------------- */
  2. /*  This file contains the window procedure for the Group Viewing window
  3.  *  for WinVN.
  4.  */
  5.  
  6. #include <windows.h>
  7. #ifndef MAC
  8. #include "winundoc.h"
  9. #endif
  10. #include "WVglob.h"
  11. #include "WinVn.h"
  12. #ifdef MAC
  13. #include "MRRM1.h"
  14. #include <DialogMgr.h>
  15. #endif
  16.  
  17. long NewCursorToTextLine (int X, int Y, TypDoc * DocPtr);
  18. long search_headers (TypDoc * HeaderDoc, TypHeader *headers, long artindex, long num_headers);
  19. LPSTR string_compare_insensitive (LPSTR a, LPSTR b);
  20.  
  21. /*--- FUNCTION: WinVnViewWndProc --------------------------------------------
  22.  *
  23.  *    Window procedure for a Group window, which contains the subjects
  24.  *    of the various articles in a newsgroup.
  25.  *    Note that there may be several different Group windows active;
  26.  *    this routine gets called any time anything happens to any of them.
  27.  */
  28.  
  29. long FAR PASCAL 
  30. WinVnViewWndProc (hWnd, message, wParam, lParam)
  31.      HWND hWnd;
  32.      UINT message;
  33.      WPARAM wParam;
  34.      LPARAM lParam;
  35. {
  36.   FARPROC lpProcAbout;
  37.   HMENU hMenu;
  38.  
  39.   PAINTSTRUCT ps;        /* paint structure          */
  40.  
  41.   HDC hDC;            /* handle to display context */
  42.   RECT clientRect;        /* selection rectangle      */
  43.   HDC hDCView;
  44.   TypDoc *ThisDoc;
  45.   int ih, j;
  46.   int iart;
  47.   long artindex;
  48.   int found;
  49.   int imemo;
  50.   int CtrlState;
  51.   TypBlock far *BlockPtr, far * ArtBlockPtr;
  52.   TypLine far *LinePtr, far * ArtLinePtr;
  53.   HANDLE hBlock;
  54.   unsigned int Offset;
  55.   char mybuf[MAXINTERNALLINE];
  56.   TypDoc *MyDoc;
  57.   TypLineID MyLineID;
  58.   POINT ptCursor;
  59.  
  60.   int X, Y, nWidth, nHeight;
  61.   int mylen;
  62.   int OldSel = FALSE;
  63.  
  64.   /* We know what *window* is being acted on, but we must find
  65.    * out which *document* is being acted on.  There's a one-to-one
  66.    * relationship between the two, and we find out which document
  67.    * corresponds to this window by scanning the GroupDocs array.
  68.    */
  69.  
  70.   for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++)
  71.     {
  72.       if (GroupDocs[ih].hDocWnd == hWnd)
  73.     {
  74.       found = TRUE;
  75.       ThisDoc = &(GroupDocs[ih]);
  76.     }
  77.     }
  78.  
  79.   if (!found)
  80.     {
  81.       ThisDoc = CommDoc;
  82.     }
  83.  
  84.   switch (message)
  85.     {
  86.     case WM_ACTIVATE:
  87.       if (wParam)
  88.     {
  89.       ActiveGroupDoc = ThisDoc;
  90. #if 0
  91.       SetMenuBar (groupMenuBar);
  92.       DrawMenuBar ();
  93. #endif
  94.     }
  95.       /* fall through */
  96.     case WM_SYSCOMMAND:
  97.       return (DefWindowProc (hWnd, message, wParam, lParam));
  98.  
  99.     case WM_SIZE:
  100.       GetClientRect (hWnd, &clientRect);
  101.       ThisDoc->ScXWidth = clientRect.right;
  102.       ThisDoc->ScYHeight = clientRect.bottom;
  103.       ThisDoc->ScYLines = (clientRect.bottom - clientRect.top - TopSpace) / LineHeight;
  104.       ThisDoc->ScXChars = (clientRect.right - clientRect.left - SideSpace) / CharWidth;
  105.       break;
  106.  
  107.     case WM_DESTROY:
  108.       /* Unlink all the article windows that belong to this group */
  109.  
  110.       UpdateSeenArts (ThisDoc);
  111.       UnlinkArtsInGroup (ThisDoc);
  112.       ThisDoc->InUse = FALSE;
  113.       if (ThisDoc == CommDoc)
  114.     {
  115.       CommBusy = FALSE;
  116.       CommDoc = (TypDoc *) NULL;
  117.     }
  118.       /* Clear the pointer in the line for this group in the   */
  119.       /* NetDoc document.  This pointer currently points       */
  120.       /* to the current document, which we are wiping out      */
  121.       /* with the destruction of this window.                  */
  122.  
  123.       LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
  124.       {
  125.     TypGroup far *group =
  126.          (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine));
  127.  
  128.     group->SubjDoc = (TypDoc *) NULL;
  129.         /* Free the header table */
  130.     if (group->header_handle)
  131.           GlobalFree (group->header_handle);
  132.     group->header_handle = (HANDLE) NULL;
  133.     group->total_headers = 0;
  134.       }
  135.  
  136.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  137.       /* Clear document                                        */
  138.       FreeDoc (ThisDoc);
  139.  
  140.       /* If there's another group window, make it the active   */
  141.       /* group window so we don't create a new one if the      */
  142.       /* New Group flag is FALSE.                              */
  143.  
  144.       for (j = MAXGROUPWNDS - 1; j >= 0; j--)
  145.     {
  146.       if (GroupDocs[j].InUse)
  147.         {
  148.           ActiveGroupDoc = &(GroupDocs[j]);
  149.           break;
  150.         }
  151.     }
  152.       break;
  153.  
  154.     case WM_KEYDOWN:
  155.       /* See if this key should be mapped to a scrolling event
  156.        * for which we have programmed the mouse.  If so,
  157.        * construct the appropriate mouse call and call the mouse code.
  158.        */
  159. #ifndef MAC
  160.       if (wParam == VK_F6)
  161.     {
  162.       NextWindow (ThisDoc);
  163.     }
  164.       else
  165.     {
  166.       CtrlState = GetKeyState (VK_CONTROL) < 0;
  167.       for (j = 0; j < NUMKEYS; j++)
  168.         {
  169.           if (wParam == key2scroll[j].wVirtKey &&
  170.           CtrlState == key2scroll[j].CtlState)
  171.         {
  172.           SendMessage (hWnd, key2scroll[j].iMessage,
  173.                    key2scroll[j].wRequest, 0L);
  174.           break;
  175.         }
  176.         }
  177.     }
  178. #endif
  179.       break;
  180.  
  181.  
  182.     case WM_CHAR:
  183.       /* Carriage Return means the same as double-clicking
  184.        * on where the cursor is currently pointing.
  185.        */
  186.       if (wParam == '\r')
  187.     {
  188.       GetCursorPos (&ptCursor);
  189.       ScreenToClient (hWnd, &ptCursor);
  190. #ifdef MAC
  191.       X = ptCursor.h;
  192.       Y = ptCursor.v;
  193. #else
  194.       X = ptCursor.x;
  195.       Y = ptCursor.y;
  196. #endif
  197.       goto getarticle;
  198.     }
  199.       else
  200.     {
  201.     }
  202.       break;
  203.  
  204.     case WM_LBUTTONDOWN:
  205.       /*  Clicking the left button on an article name toggles the
  206.        *  selected/not selected status of that article.
  207.        *  Currently selected articles are displayed in reverse video.
  208.        */
  209.       break;
  210.  
  211.     case WM_LBUTTONDBLCLK:
  212.       /*  Double-clicking on an article subject creates an "Article"
  213.        *  window, whose purpose is to display the article.
  214.        */
  215.       X = LOWORD (lParam);
  216.       Y = HIWORD (lParam);
  217.     getarticle:;
  218.  
  219.       artindex = NewCursorToTextLine (X, Y, ThisDoc);
  220.     if (artindex > -1 )
  221.     {
  222.       ViewArticle (ThisDoc, artindex, FALSE);
  223.     }
  224.  
  225.       break;
  226.  
  227.     case WM_VSCROLL:
  228.       NewScrollIt (ThisDoc, wParam, lParam);
  229.       break;
  230.  
  231.     case WM_PAINT:
  232.       {
  233.     HANDLE hBlock;
  234.     int MyLen, width;
  235.     unsigned int Offset;
  236.     int VertLines, HorzChars;
  237.     int EndofDoc = FALSE;
  238.     int RangeHigh, CurPos;
  239.     int RestX, indicatorwidth, Xtext;
  240.     char far *textptr;
  241.     char indicator;
  242.     char *indcptr;
  243.     TypArticle far *MyArt;
  244.     unsigned int artindex;
  245.           long int artnum;
  246.     TypBlock far *BlockPtr;
  247.     TypLine far *LinePtr;
  248.     TypBlock far *NetBlockPtr;
  249.     TypLine far *NetLinePtr;
  250.     TypGroup far *group;
  251.           TypHeader *headers;
  252.     HANDLE header_handle;
  253.           char scratch_line [MAXINTERNALLINE];
  254.     long int OldHighestSeen;
  255.     HANDLE hBlackBrush;
  256.     DWORD MyColors[4], MyBack[4];
  257. #ifdef MAC
  258.     RECT myRect;
  259.     POINT myPoint;
  260. #endif
  261.     DWORD Rop;
  262.     int MyColorMask = 0;
  263.     int PrevColorMask = MyColorMask;
  264.     SIZE string_size;
  265.  
  266.     /* MyColors and MyBack are arrays of colors used to display text
  267.          * foreground and background.
  268.          * The ColorMask variables are indices into these arrays.
  269.          * We set and clear bits in these indices depending upon
  270.          * whether the article has been selected or seen.
  271.          */
  272.  
  273. #define SEEN_MASK 1
  274. #define SELECT_MASK 2
  275.     hDC = BeginPaint (hWnd, &ps);
  276.  
  277.     GetClientRect (hWnd, &clientRect);
  278.     SelectObject (hDC, hFont);
  279.  
  280.     VertLines = ( (ThisDoc->ScYLines > (ThisDoc->TotalLines - ThisDoc->TopLineOrd))
  281.                      ? (ThisDoc->TotalLines - ThisDoc->TopLineOrd )
  282.                      : ThisDoc->ScYLines);
  283.  
  284.     HorzChars = ThisDoc->ScXChars;
  285.  
  286.     MyColors[0] = GetTextColor (hDC);    /* black */
  287.     MyColors[1] = GroupSeenColor;
  288.     MyColors[2] = GetBkColor (hDC);    /* white */
  289.     MyColors[3] = MyColors[1];    /* blue  */
  290.  
  291.     MyBack[0] = MyColors[2];/* white */
  292.     MyBack[1] = MyColors[2];
  293.     MyBack[2] = MyColors[0];
  294.     MyBack[3] = MyColors[0];
  295.  
  296. /*    LockLine (ThisDoc->hCurTopScBlock, ThisDoc->TopScOffset, ThisDoc->TopScLineID,
  297.           &BlockPtr, &LinePtr);
  298. */
  299.     /* Update the scroll bar thumb position.                 */
  300.  
  301.  
  302.     CurPos = ThisDoc->TopLineOrd;
  303.     if (CurPos < 0)
  304.       CurPos = 0;
  305.     RangeHigh = ThisDoc->TotalLines - VertLines;
  306.     if (RangeHigh < 0)
  307.       RangeHigh = 0;
  308.     SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE);
  309.     SetScrollPos (hWnd, SB_VERT, CurPos, TRUE);
  310.  
  311.     GetTextExtentPoint (hDC, "s 99999 ", 8, &string_size);
  312.           indicatorwidth = string_size.cx;
  313.  
  314.     LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
  315.           &NetBlockPtr, &NetLinePtr);
  316.     group = (TypGroup far *) ((char far *) NetLinePtr + sizeof (TypLine));
  317.  
  318.         header_handle = group->header_handle;
  319.  
  320.           if (header_handle)
  321.           headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *));
  322.  
  323.     OldHighestSeen = group->HighestPrevSeen;
  324.     UnlockLine (NetBlockPtr, NetLinePtr, &hBlock, &Offset, &MyLineID);
  325.  
  326. #ifdef MAC
  327.     myRect.right = ThisDoc->DocClipRect.right;
  328.     myRect.top = 0;
  329.     myRect.bottom = LineHeight;
  330. #endif
  331.  
  332.     /* Now paint this stuff on the screen.               */
  333.  
  334.     X = SideSpace;
  335.     Xtext = X + indicatorwidth;
  336.     Y = StartPen;
  337.  
  338.     artindex = ThisDoc->TopLineOrd;
  339.  
  340.           if (ThisDoc->TotalLines)
  341.           do
  342.         {
  343.           artnum = headers[artindex].number;
  344.  
  345.             indicator = ' ';
  346.                    if(headers[artindex].Seen) {
  347.                        indicator = 's';
  348.                    } else if(OldHighestSeen) {
  349.                        if(headers[artindex].number > OldHighestSeen) {
  350.                            indicator = 'n';
  351.                        }
  352.                    }
  353.  
  354.                if(ThisDoc->FindOffset == artindex)
  355.                  indicator = '>';
  356.  
  357.             sprintf (scratch_line, "%c %u %6.6Fs %20.20Fs %4Fd %Fs ",
  358.                     indicator,
  359.                     headers[artindex].number,
  360.                     headers[artindex].date,
  361.                     headers[artindex].from,
  362.                 headers[artindex].lines,
  363.                     headers[artindex].subject);
  364.  
  365.           MyLen = lstrlen (scratch_line);
  366.  
  367.           /* Figure out the color of this line.                 */
  368.  
  369.           if (headers[artindex].Seen)
  370.         {
  371.           MyColorMask |= SEEN_MASK;
  372.         }
  373.           else
  374.         {
  375.           MyColorMask &= (0xff - SEEN_MASK);
  376.         }
  377.           if (headers[artindex].Selected)
  378.         {
  379.           MyColorMask |= SELECT_MASK;
  380.           Rop = BLACKNESS;
  381.         }
  382.           else
  383.         {
  384.           MyColorMask &= 0xff - SELECT_MASK;
  385.           Rop = WHITENESS;
  386.         }
  387.           if (MyColorMask != PrevColorMask)
  388.         {
  389.           SetTextColor (hDC, MyColors[MyColorMask]);
  390.           SetBkColor (hDC, MyBack[MyColorMask]);
  391.           PrevColorMask = MyColorMask;
  392.         }
  393.  
  394.           /* Now write out the line.                            */
  395.  
  396.           GetTextExtentPoint (hDC, scratch_line, MyLen, &string_size);
  397.           width = string_size.cx;
  398.  
  399.  
  400.           TextOut (hDC, X, Y, scratch_line, MyLen);
  401. #ifdef MAC
  402.           GetPen (&myPoint);
  403.           myRect.left = myPoint.h;
  404.           FillRect (&myRect, white);
  405.  
  406.           myRect.top += LineHeight;
  407.           myRect.bottom += LineHeight;
  408. #else
  409.           RestX = X + width;
  410.           PatBlt (hDC, RestX, Y, clientRect.right - RestX, LineHeight, Rop);
  411. #endif
  412. #if 0
  413.           if (MyLen < HorzChars)
  414.         {
  415.           RestX = X + width;
  416.           /*    TextOut(hDC,RestX,Y,Blanks,MAXINTERNALLINE); */
  417.           PatBlt (hDC, RestX, Y, clientRect.right - RestX, LineHeight, Rop);
  418.         }
  419. #endif
  420.           Y += LineHeight;
  421.  
  422.         artindex++;
  423.  
  424.         }
  425.       while (--VertLines > 0 );
  426.  
  427.           if (header_handle)
  428.             GlobalUnlock (header_handle);
  429.  
  430.     /* We've reached the end of the data to be displayed     */
  431.     /* on this window.  If there's more screen real estate   */
  432.     /* left, just blank it out.                              */
  433.  
  434.     SetTextColor (hDC, MyColors[0]);
  435.     SetBkColor (hDC, MyBack[0]);
  436.     PatBlt (hDC, 0, Y, clientRect.right, clientRect.bottom - Y, PATCOPY);
  437.  
  438.     EndPaint (hWnd, &ps);
  439.     break;
  440.       }
  441.  
  442.     case WM_COMMAND:
  443.       switch (wParam)
  444.     {
  445.     case IDV_EXIT:
  446.       DestroyWindow (hWnd);
  447.       break;
  448.  
  449.     case IDV_NEXT:
  450.       break;
  451.  
  452.     case IDM_FIND:
  453.     case IDM_FIND_NEXT_SAME:
  454.  
  455.           FindDoc = ThisDoc;
  456.  
  457.       if (wParam == IDM_FIND)
  458.             DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg);
  459.           if(strcmp(FindDoc->SearchStr,"")) {
  460.           TypGroup far * group;
  461.           TypHeader  *headers;
  462.           HANDLE header_handle;
  463.           int starting_at;
  464.  
  465.           LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID,
  466.             &BlockPtr, &LinePtr);
  467.           group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  468.  
  469.           header_handle = group->header_handle;
  470.           headers = (TypHeader  *) ((char *) GlobalLock (header_handle) + sizeof(char  *));
  471.  
  472.           UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  473.  
  474.           /* 'Find Next' will start one line after */
  475.           starting_at = ThisDoc->TopLineOrd + ((wParam == IDM_FIND) ? 0 : 1);
  476.  
  477.           /* back up one if we're at the end */
  478.           if (starting_at >= group->total_headers)
  479.             starting_at--;
  480.  
  481.           found = search_headers (ThisDoc, headers, starting_at, group->total_headers);
  482.            if (found == -1)
  483.         {
  484.           strcpy (mybuf, "\"");
  485.           strcat (mybuf, ThisDoc->SearchStr);
  486.           strcat (mybuf, "\" not found.");
  487.           MessageBox (hWnd, mybuf, "Not found", MB_OK);
  488.         }
  489.                else {
  490.                  ThisDoc->TopLineOrd = found;
  491.                  ThisDoc->FindOffset = found;
  492.                  InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE);
  493.                }
  494.  
  495.             GlobalUnlock (header_handle);
  496.           }
  497.       break;
  498.  
  499.     case IDV_CREATE:
  500.       /* We are creating the skeleton text of a new posting.
  501.            * Most of the work is done by CreatePostingWnd and
  502.            * CreatePostingText.  Here we have to identify
  503.            * the newsgroup for those routines.
  504.            * Get the newsgroup from the line in NetDoc that
  505.            * points to this document.
  506.            */
  507.       LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset,
  508.             ThisDoc->ParentLineID, &BlockPtr, &LinePtr);
  509.       ExtractTextLine (ThisDoc->ParentDoc, LinePtr,
  510.                mybuf, MAXINTERNALLINE);
  511.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  512.       NewsgroupsPtr = mybuf;
  513.       CreatePostingWnd ((TypDoc *) NULL, DOCTYPE_POSTING);
  514.       break;
  515.     }
  516.       break;
  517.  
  518.  
  519.     default:
  520.       return (DefWindowProc (hWnd, message, wParam, lParam));
  521.     }
  522.   return (0);
  523. }
  524.  
  525.  
  526. /* --- function CopyNonBlank ----------------------------------------
  527.  *
  528.  *    Copy a string, terminating at the first blank or zero byte.
  529.  *
  530.  *    Entry    strtarg     FWA of target area.
  531.  *             strsource   FWA of source string.
  532.  *             maxchars    the maximum number of characters to copy
  533.  *                         if no blank or zero is encountered.
  534.  *
  535.  *    Exit     returns the number of characters copied.
  536.  *
  537.  *    I believe this routine is no longer used, but I'm leaving it here.
  538.  */
  539.  
  540. int
  541. CopyNonBlank (strtarg, strsource, maxchars)
  542.      char *strtarg, *strsource;
  543.      int maxchars;
  544. {
  545.   int j;
  546.  
  547.   for (j = 0; j < maxchars && strsource[j] != ' '; j++)
  548.     strtarg[j] = strsource[j];
  549.   return (j);
  550. }
  551.  
  552. /* --- function FileLength -------------------------------------------
  553.  *
  554.  *    Find the size, in bytes, of a file.
  555.  *
  556.  *    Entry    hFile    handle of the file in question.
  557.  *
  558.  *    Exit     returns the length of the file in bytes.
  559.  *
  560.  *    This routine is no longer used.
  561.  */
  562.  
  563. long
  564. FileLength (hFile)
  565.      HANDLE hFile;
  566. {
  567.   long lCurrentPos = _llseek (hFile, 0L, 1);
  568.   long lFileLength = _llseek (hFile, 0L, 2);
  569.  
  570.   _llseek (hFile, lCurrentPos, 0);
  571.  
  572.   return lFileLength;
  573.  
  574. }
  575.  
  576. /*-- function ViewArticle -------------------------------------------------
  577.  *
  578.  *  View a given article.   Either create a new window for it or
  579.  *  recycle an existing window.
  580.  *  This function requests an article from the server, so there
  581.  *  must not already be a transaction in progress.
  582.  *
  583.  *    Entry    Doc            points to the document for this group.
  584.  *           artindex       index into header array for this group.
  585.  *             Reuse          is TRUE if we ought to reuse the
  586.  *                            currently active article window (if any).
  587.  */
  588. void
  589. ViewArticle (Doc, artindex, Reuse)
  590.      TypDoc *Doc;
  591.      long artindex;
  592.      BOOL Reuse;
  593. {
  594.   TypDoc *MyDoc;
  595.   TypGroup far *GroupDoc;
  596.   BOOL newdoc;
  597.   BOOL found;
  598.   int docnum;
  599.   HANDLE hBlock;
  600.   unsigned int Offset;
  601.   TypLineID MyLineID;
  602.   TypBlock far *BlockPtr;
  603.   TypLine far *LinePtr;
  604.   HWND hWndArt;
  605.   int width;
  606.   char mybuf[MAXINTERNALLINE];
  607.   long int artnum,oldindex;
  608.   char far *lpsz;
  609.   HWND hWndGroup = Doc->hDocWnd;
  610.   char far *lpszGroupName;
  611.   HANDLE header_handle;
  612.   TypHeader  * headers;
  613.  
  614.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  615.             &BlockPtr, &LinePtr);
  616.  
  617.   GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  618.  
  619.   header_handle = GroupDoc->header_handle;
  620.   headers = (TypHeader  *) ((char *)GlobalLock (header_handle) + sizeof(char  *)) ;
  621.  
  622.   if (MyDoc = headers[artindex].ArtDoc)
  623.     {
  624.  
  625.       /* We already have a document containing the article */
  626.       /* so just activate it.                */
  627.  
  628.       /*  ShowWindow(MyDoc->hDocWnd,SW_SHOW); */
  629.       SetActiveWindow (MyDoc->hDocWnd);
  630.       SetFocus (MyDoc->hDocWnd);
  631.       goto endit;
  632.     }
  633.   if (CommBusy)
  634.     {
  635.       MessageBox (hWndGroup, "Sorry, I am already busy retrieving information from the server.\n\
  636. Try again in a little while.", "Can't request text of article", MB_OK | MB_ICONASTERISK);
  637.       goto endit;
  638.     }
  639.  
  640.   newdoc = FALSE;
  641.   if ((NewArticleWindow && !Reuse) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse))
  642.     {
  643.       found = FALSE;
  644.       for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++)
  645.     {
  646.       if (!ArticleDocs[docnum].InUse)
  647.         {
  648.           found = TRUE;
  649.           newdoc = TRUE;
  650.           CommDoc = &(ArticleDocs[docnum]);
  651.           break;
  652.         }
  653.     }
  654.       if (!found)
  655.     {
  656.       MessageBox (hWndGroup, "You have too many article windows \
  657. active;\nClose one or uncheck the option \"New Window for each Article\".", "Can't open new window", MB_OK | MB_ICONASTERISK);
  658.       goto endit;
  659.     }
  660.     }
  661.   else
  662.     {
  663.       /* Must reuse old window for this article.         */
  664.       CommDoc = ActiveArticleDoc;
  665.       if (CommDoc->ParentDoc == Doc) {
  666.         oldindex = CommDoc->ParentOffset;    /* more slot abusage */
  667.         headers[oldindex].ArtDoc = (TypDoc *) NULL;
  668.       }
  669.       /* clear out old doc */
  670.       FreeDoc (CommDoc);
  671.     }
  672.  
  673.   headers[artindex].Seen = TRUE;
  674.  
  675.   InvalidateRect (hWndGroup, NULL, FALSE);
  676.  
  677.   lpsz = (char far *) headers[artindex].subject;
  678.   strcpy (mybuf, "Retrieving \"");
  679.   lstrcat (mybuf, lpsz);
  680.   lstrcat (mybuf, "\"");
  681.  
  682.   if (newdoc)
  683.     {
  684.       if (xScreen > 88 * CharWidth)
  685.     {
  686.       width = 88 * CharWidth;
  687.     }
  688.       else
  689.     {
  690.       width = xScreen - 1 * CharWidth;
  691.     }
  692.       hWndArt = CreateWindow ("WinVnArt",
  693.                   mybuf,
  694.                   WS_OVERLAPPEDWINDOW | WS_VSCROLL ,
  695.                   xScreen - (width + (docnum) * CharWidth),    /* Initial X pos */
  696.                 (int) (yScreen * 3 / 8) + (docnum) * LineHeight,    /* Initial Y pos */
  697.                   (int) width,    /* Initial X Width */
  698.                   (int) (yScreen * 5 / 8) - (1 * LineHeight),    /* Initial Y height */
  699.                   NULL,
  700.                   NULL,
  701.                   hInst,
  702.                   NULL);
  703.  
  704.       if (!hWndArt)
  705.     return;            /* ??? */
  706. #ifndef MAC
  707.       ShowWindow (hWndArt, SW_SHOWNORMAL);
  708. #else
  709.       MyShowWindow (hWndArt, SW_SHOWNORMAL);
  710. #endif
  711.     }
  712.   else
  713.     {
  714.       hWndArt = CommDoc->hDocWnd;
  715.       SetWindowText (hWndArt, mybuf);
  716.     }
  717.  
  718.   /*  Now that we have created the window, create the corresponding
  719.    *  document, and make the new window active.
  720.    */
  721.  
  722.   InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);
  723.  
  724.   CommDoc->InUse = TRUE;
  725.   CommDoc->LastSeenLineID = artindex;    /* Keep an index with the article */
  726.  
  727.   SetActiveWindow (hWndArt);
  728.   SetFocus (hWndArt);
  729.  
  730.   headers[artindex].ArtDoc = CommDoc;
  731.   CommDoc->ParentOffset = artindex;
  732.   InvalidateRect (hWndArt, NULL, FALSE);
  733.   UpdateWindow (hWndArt);
  734.  
  735.   CommLinePtr = CommLineIn;
  736.   CommBusy = TRUE;
  737.   CommState = ST_ARTICLE_RESP;
  738.  
  739.  
  740.   /* If we're not already in this group on the server,
  741.    * send out a GROUP command for this window so we get back
  742.    * into the right Group.
  743.    */
  744.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  745.         &BlockPtr, &LinePtr);
  746.   lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
  747.   if (lstrcmp (CurrentGroup, lpszGroupName))
  748.     {
  749.       CommState = ST_GROUP_REJOIN;
  750.       strcpy (mybuf, "GROUP ");
  751.       lstrcat (mybuf, lpszGroupName);
  752.       /* lstrcat(mybuf,"\r"); */
  753.       mylstrncpy (CurrentGroup, lpsz, MAXGROUPNAME);
  754.       PutCommLine (mybuf, lstrlen (mybuf));
  755.     }
  756.  
  757.   UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  758.  
  759.   artnum = headers[artindex].number;
  760.  
  761.   sprintf (mybuf, "ARTICLE %ld\r", artnum);
  762.   PutCommLine (mybuf, lstrlen (mybuf));
  763.  
  764. endit:
  765.     GlobalUnlock (header_handle);
  766. }
  767.  
  768. void
  769. view_article_by_message_id (Doc, article_request, artindex)
  770.      TypDoc *Doc;
  771.      char far * article_request;
  772.      long artindex;
  773. {
  774.   TypDoc *MyDoc;
  775.   TypGroup far *GroupDoc;
  776.   BOOL newdoc;
  777.   BOOL found;
  778.   int docnum;
  779.   HANDLE hBlock;
  780.   unsigned int Offset;
  781.   TypLineID MyLineID;
  782.   TypBlock far *BlockPtr;
  783.   TypLine far *LinePtr;
  784.   HWND hWndArt;
  785.   int width;
  786.   char mybuf[MAXINTERNALLINE];
  787.   long int artnum;
  788.   char far *lpsz;
  789.   HWND hWndGroup = Doc->hDocWnd;
  790.   char far *lpszGroupName;
  791.   HANDLE header_handle;
  792.   TypHeader  * headers;
  793.  
  794.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  795.             &BlockPtr, &LinePtr);
  796.  
  797.   GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  798.  
  799.   header_handle = GroupDoc->header_handle;
  800.   headers = (TypHeader  *) ((char *) GlobalLock (header_handle) + sizeof(char  *)) ;
  801.  
  802.   if (CommBusy)
  803.     {
  804.       MessageBox (hWndGroup, "Sorry, I am already busy retrieving information from the server.\n\
  805. Try again in a little while.", "Can't request text of article", MB_OK | MB_ICONASTERISK);
  806.       goto endit;
  807.     }
  808.  
  809.   newdoc = FALSE;
  810.   if ((NewArticleWindow) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse))
  811.     {
  812.       found = FALSE;
  813.       for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++)
  814.     {
  815.       if (!ArticleDocs[docnum].InUse)
  816.         {
  817.           found = TRUE;
  818.           newdoc = TRUE;
  819.           CommDoc = &(ArticleDocs[docnum]);
  820.           break;
  821.         }
  822.     }
  823.       if (!found)
  824.     {
  825.       MessageBox (hWndGroup, "You have too many article windows \
  826. active;\nClose one or uncheck the option \"New Window for each Article\".", "Can't open new window", MB_OK | MB_ICONASTERISK);
  827.       goto endit;
  828.     }
  829.     }
  830.   else
  831.     {
  832.       /* Must reuse old window for this article.         */
  833.       CommDoc = ActiveArticleDoc;
  834.       /* sever the article/artindex connection */
  835.       headers[CommDoc->ParentOffset].ArtDoc = (TypDoc *) NULL;
  836.       /* clear out old doc */
  837.       FreeDoc (CommDoc);
  838.     }
  839.  
  840.   headers[artindex].Seen = TRUE;
  841.  
  842.   InvalidateRect (hWndGroup, NULL, FALSE);
  843.  
  844.   strcpy (mybuf, "Retrieving \"");
  845.   lstrcat (mybuf, article_request);
  846.   lstrcat (mybuf, "\"");
  847.  
  848.   if (newdoc)
  849.     {
  850.       if (xScreen > 88 * CharWidth)
  851.     {
  852.       width = 88 * CharWidth;
  853.     }
  854.       else
  855.     {
  856.       width = xScreen - 1 * CharWidth;
  857.     }
  858.       hWndArt = CreateWindow ("WinVnArt",
  859.                   mybuf,
  860.                   WS_OVERLAPPEDWINDOW | WS_VSCROLL ,
  861.                   xScreen - (width + (docnum) * CharWidth),    /* Initial X pos */
  862.                 (int) (yScreen * 3 / 8) + (docnum) * LineHeight,    /* Initial Y pos */
  863.                   (int) width,    /* Initial X Width */
  864.                   (int) (yScreen * 5 / 8) - (1 * LineHeight),    /* Initial Y height */
  865.                   NULL,
  866.                   NULL,
  867.                   hInst,
  868.                   NULL);
  869.  
  870.       if (!hWndArt)
  871.     return;            /* ??? */
  872. #ifndef MAC
  873.       ShowWindow (hWndArt, SW_SHOWNORMAL);
  874. #else
  875.       MyShowWindow (hWndArt, SW_SHOWNORMAL);
  876. #endif
  877.     }
  878.   else
  879.     {
  880.       hWndArt = CommDoc->hDocWnd;
  881.       SetWindowText (hWndArt, mybuf);
  882.     }
  883.  
  884.   /*  Now that we have created the window, create the corresponding
  885.    *  document, and make the new window active.
  886.    */
  887.  
  888.   InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE);
  889.  
  890.   CommDoc->InUse = TRUE;
  891.   CommDoc->LastSeenLineID = artindex;    /* Keep an index with the article */
  892.  
  893.   SetActiveWindow (hWndArt);
  894.   SetFocus (hWndArt);
  895.  
  896.   headers[artindex].ArtDoc = CommDoc;
  897.   InvalidateRect (hWndArt, NULL, FALSE);
  898.   UpdateWindow (hWndArt);
  899.  
  900.   CommLinePtr = CommLineIn;
  901.   CommBusy = TRUE;
  902.   CommState = ST_ARTICLE_RESP;
  903.  
  904.  
  905.   /* If we're not already in this group on the server,
  906.    * send out a GROUP command for this window so we get back
  907.    * into the right Group.
  908.    */
  909.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  910.         &BlockPtr, &LinePtr);
  911.   lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
  912.   if (lstrcmp (CurrentGroup, lpszGroupName))
  913.     {
  914.       CommState = ST_GROUP_REJOIN;
  915.       strcpy (mybuf, "GROUP ");
  916.       lstrcat (mybuf, lpszGroupName);
  917.       /* lstrcat(mybuf,"\r"); */
  918.       mylstrncpy (CurrentGroup, lpsz, MAXGROUPNAME);
  919.       PutCommLine (mybuf, lstrlen (mybuf));
  920.     }
  921.  
  922.   UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  923.  
  924.   artnum = headers[artindex].number;
  925.  
  926.   lstrcpy (mybuf, article_request);
  927.   PutCommLine (mybuf, lstrlen (mybuf));
  928.  
  929. endit:
  930.     GlobalUnlock (header_handle);
  931. }
  932.  
  933. /*-- Function UnlinkArtsInGroup ---------------------------------------
  934.  *
  935.  *  Modify all the article documents and all the article windows currently
  936.  *  associated with a group so that none of them points to that group.
  937.  *  Used when the group window is going away or is being recycled.
  938.  *
  939.  *    Entry    GroupDoc    points to the document to which references
  940.  *                         should be eliminated.
  941.  */
  942. void
  943. UnlinkArtsInGroup (GroupDoc)
  944.      TypDoc *GroupDoc;
  945. {
  946.   int iart;
  947.  
  948.   for (iart = 0; iart < MAXARTICLEWNDS; iart++)
  949.     {
  950.       if (ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc)
  951.     {
  952.       ArticleDocs[iart].ParentDoc = (TypDoc *) NULL;
  953.       ArticleDocs[iart].hParentBlock = 0;
  954.     }
  955.     }
  956. }
  957.  
  958. /*--- function UpdateSeenArts -------------------------------------------
  959.  *
  960.  *  Given a Group document, update the TypGroup line for
  961.  *  that document in the Net document with respect to which
  962.  *  articles have been seen.
  963.  *  This routine would typically be called just before a Group document
  964.  *  is going to be destroyed or erased.  That would be the time to
  965.  *  take the information in the TypArticle structures of each line
  966.  *  in the document and transfer it to the line in the NetDoc document
  967.  *  corresponding to this group.
  968.  *
  969.  *  This routine has to take information of the form:
  970.  *    123:Unseen;  124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen
  971.  *  found in the TypArticle structures in consecutive lines in the document
  972.  *  and transform it to the general form used by .newsrc files:
  973.  *    124,127-129
  974.  *  (though we are using our internal representation & not ASCII characters).
  975.  *
  976.  *    Entry    Doc      points to the document for this group.
  977.  *
  978.  *    Exit     The line in the Net document corresponding to this
  979.  *              group has been updated.
  980.  */
  981. void
  982. UpdateSeenArts (Doc)
  983.      TypDoc *Doc;
  984. {
  985.   TypRange MyRange, *RangePtr;
  986.   TypGroup *group;
  987.   TypLine far *LinePtr, far * ParentLine;
  988.   TypBlock far *BlockPtr, far * ParentBlock;
  989.   HANDLE hLine,header_handle;
  990.   TypLine *LocalLinePtr;
  991.   TypHeader  * headers;
  992.   TypArticle far *Art;
  993.   BOOL InSeen = TRUE;
  994.   unsigned int MyLength;
  995.   unsigned int maxRanges;
  996.   long artindex;
  997.  
  998.   /*  Get the line in the Net document that corresponds to this
  999.    *  group.  Make a local copy of it and set RangePtr to point to
  1000.    *  the first range in that line.  We will ignore the old line's
  1001.    *  "seen" data and create the information afresh from what we
  1002.    *  have in this document.
  1003.    */
  1004.   LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID,
  1005.         &ParentBlock, &ParentLine);
  1006.  
  1007.   hLine = LocalAlloc (LMEM_MOVEABLE, BLOCK_SIZE);
  1008.   LocalLinePtr = (TypLine *) LocalLock (hLine);
  1009.   group = (TypGroup *) ((char *) LocalLinePtr + sizeof (TypLine));
  1010.  
  1011.   MoveBytes (ParentLine, LocalLinePtr, ParentLine->length);
  1012.  
  1013.   header_handle = group->header_handle;
  1014.   headers = (TypHeader  *) ((char *) GlobalLock (header_handle) + sizeof (char  *));
  1015.  
  1016.   group->nRanges = 0;
  1017.  
  1018.   maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
  1019.            group->nRanges * sizeof (TypRange)) / sizeof (TypRange) - 1;
  1020.  
  1021.   RangePtr = (TypRange *) ((char *) LocalLinePtr + sizeof (TypLine) +
  1022.                RangeOffset (group->NameLen));
  1023.   MyRange.First = 1;
  1024.  
  1025.   /* Get the first line in this document.
  1026.    * If it cannot be found, just set Last=First and skip the
  1027.    * proceeding processing.  Otherwise, assume we've seen everything
  1028.    * up to but not including the first article in the document.
  1029.    */
  1030.  
  1031. /*  LockLine (Doc->hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr); */
  1032.  
  1033.   artindex = 0;
  1034.   if (!Doc->TotalLines)
  1035.     {
  1036.       MyRange.Last = 1;
  1037.     }
  1038.   else
  1039.     {
  1040.       MyRange.Last = headers[artindex].number - 1;
  1041.  
  1042.       /* Loop to scan through the document, fabricating article ranges.
  1043.        */
  1044.       do
  1045.     {
  1046.       if (headers[artindex].Seen)
  1047.         {
  1048.           if (InSeen)
  1049.         {
  1050.           /* Continuing a sequence of seen articles.            */
  1051.           MyRange.Last = headers[artindex].number;
  1052.         }
  1053.           else
  1054.         {
  1055.           /* Starting a new sequence of seen articles.          */
  1056.           MyRange.First = headers[artindex].number;
  1057.           MyRange.Last = headers[artindex].number;
  1058.           InSeen = TRUE;
  1059.         }
  1060.         }
  1061.       else
  1062.         {
  1063.           if (InSeen)
  1064.         {
  1065.           /* Ending a sequence of seen articles.                   */
  1066.           InSeen = FALSE;
  1067.           *(RangePtr++) = MyRange;
  1068.           (group->nRanges)++;
  1069.         }
  1070.           else
  1071.         {
  1072.           /* Continuing a sequence of unseen articles.             */
  1073.         }
  1074.         }
  1075.     }
  1076.     while ((group->nRanges < maxRanges) &&
  1077.            ((++artindex < Doc->TotalLines)));
  1078.  
  1079.       if (InSeen)
  1080.     {
  1081.       *(RangePtr++) = MyRange;
  1082.       (group->nRanges)++;
  1083.     }
  1084.     }
  1085.  
  1086.     MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) +
  1087.            sizeof (TypRange) * (group->nRanges) + sizeof (int);
  1088.  
  1089.   LocalLinePtr->length = MyLength;
  1090.   *(int *) ((char *) LocalLinePtr + MyLength - sizeof (int)) = MyLength;
  1091.  
  1092.   ReplaceLine (LocalLinePtr, &ParentBlock, &ParentLine);
  1093.   GlobalUnlock (ParentBlock->hCurBlock);
  1094.  
  1095.   LocalUnlock (hLine);
  1096.   LocalFree (hLine);
  1097.  
  1098. }
  1099.  
  1100.  
  1101. /*-- function CursorToTextLine ----------------------------------------
  1102.  *
  1103.  *   Routine to locate a text line in a document, based on the
  1104.  *   cursor position.  Used to figure out which line is being selected
  1105.  *   when a user clicks a mouse button.
  1106.  *
  1107.  *   Entry    X, Y    are the position of the cursor.
  1108.  *            DocPtr  points to the current document.
  1109.  *
  1110.  *   Exit     *LinePtr points to the current line, if one was found.
  1111.  *            *BlockPtr points to the current block, if found.
  1112.  *            Function returns TRUE iff a line was found that corresponds
  1113.  *              to the cursor position.
  1114.  */
  1115. long
  1116. NewCursorToTextLine (X, Y, DocPtr)
  1117.      int X;
  1118.      int Y;
  1119.      TypDoc *DocPtr;
  1120. {
  1121.   int found;
  1122.   int SelLine;
  1123.  
  1124.   if (Y < TopSpace || Y > TopSpace + DocPtr->ScYLines * LineHeight ||
  1125.       X < SideSpace)
  1126.     {
  1127.       /* Cursor is in no-man's-land at edge of window.               */
  1128.     return(-1);
  1129.     }
  1130.   else
  1131.     {
  1132.       SelLine = (Y - TopSpace) / LineHeight;
  1133.     return ( DocPtr->TopLineOrd + SelLine);
  1134.     }
  1135. }
  1136.  
  1137. long
  1138. search_headers (TypDoc * HeaderDoc,
  1139.                 TypHeader  *headers,
  1140.                 long artindex,
  1141.                 long num_headers)
  1142. {
  1143.  
  1144.     do {
  1145.     if (string_compare_insensitive (headers[artindex].subject,
  1146.                                         HeaderDoc->SearchStr))
  1147.            return (artindex);        /* return the index */
  1148.     } while (artindex++ < (num_headers - 1));
  1149.  
  1150.     return (-1);            /* not found */
  1151. }
  1152.  
  1153. LPSTR
  1154. string_compare_insensitive (LPSTR a, LPSTR b)
  1155. {
  1156.   int lena = lstrlen (a);
  1157.   int lenb = lstrlen (b);
  1158.   int count;
  1159.  
  1160.   for ( count = lena - lenb + 1 ; count > 0 ; count--, a++)
  1161.    if (_strnicmp (a,b,lenb) == 0)
  1162.      return a;
  1163.  
  1164.   return (NULL);
  1165. }
  1166.